package com.itaming.lycheeframework.support.utils;

import lombok.experimental.UtilityClass;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyAccessorFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

/**
 * Bean工具类
 *
 * @author A.Ming
 */
@UtilityClass
public class BeanUtil extends BeanUtils {

    /**
     * 复制对象属性
     *
     * @param source      源对象
     * @param targetClass 目标类
     * @return 目标对象
     * @throws BeansException
     */
    public static <S, T> T copy(S source, Class<T> targetClass) throws BeansException {
        return copy(source, targetClass, null);
    }

    /**
     * 复制对象属性
     *
     * @param source      源对象
     * @param targetClass 目标类
     * @param operation   附加操作
     * @return 目标对象
     * @throws BeansException
     */
    public static <S, T> T copy(S source, Class<T> targetClass, BiConsumer<S, T> operation) throws BeansException {
        return copy(source, () -> instantiateClass(targetClass), operation);
    }

    /**
     * 复制对象属性
     *
     * @param source         源对象
     * @param targetSupplier 目标对象提供者
     * @return 目标对象
     * @throws BeansException
     */
    public static <S, T> T copy(S source, Supplier<T> targetSupplier) throws BeansException {
        return copy(source, targetSupplier, null);
    }

    /**
     * 复制对象属性
     *
     * @param source         源对象
     * @param targetSupplier 目标对象提供者
     * @param operation      附加操作
     * @return 目标对象
     * @throws BeansException
     */
    public static <S, T> T copy(S source, Supplier<T> targetSupplier, BiConsumer<S, T> operation) throws BeansException {
        if (source == null || targetSupplier == null) {
            return null;
        }

        T t = targetSupplier.get();
        if (t == null) {
            return null;
        }

        copyProperties(source, t);

        if (operation != null) {
            operation.accept(source, t);
        }

        return t;
    }

    /**
     * 复制列表对象属性
     *
     * @param source      源对象列表
     * @param targetClass 目标类
     * @return 目标对象列表
     * @throws BeansException
     */
    public static <S, T> List<T> copyList(List<S> source, Class<T> targetClass) throws BeansException {
        return copyList(source, targetClass, null);
    }

    /**
     * 复制列表对象属性
     *
     * @param source      源对象列表
     * @param targetClass 目标类
     * @param operation   附加操作
     * @return 目标对象列表
     * @throws BeansException
     */
    public static <S, T> List<T> copyList(List<S> source, Class<T> targetClass, BiConsumer<S, T> operation) throws BeansException {
        return copyList(source, () -> instantiateClass(targetClass), operation);
    }

    /**
     * 复制列表对象属性
     *
     * @param source         源对象列表
     * @param targetSupplier 目标对象提供者
     * @return 目标对象列表
     * @throws BeansException
     */
    public static <S, T> List<T> copyList(List<S> source, Supplier<T> targetSupplier) throws BeansException {
        return copyList(source, targetSupplier, null);
    }

    /**
     * 复制列表对象属性
     *
     * @param source         源对象列表
     * @param targetSupplier 目标对象提供者
     * @param operation      附加操作
     * @return 目标对象列表
     * @throws BeansException
     */
    public static <S, T> List<T> copyList(List<S> source, Supplier<T> targetSupplier, BiConsumer<S, T> operation) throws BeansException {
        List<T> list = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(source)) {
            for (S o : source) {
                T t = copy(o, targetSupplier, operation);
                if (t != null) {
                    list.add(t);
                }
            }
        }

        return list;
    }

    /**
     * 获取对象属性, 支持多级属性名<code>a.b.c</code>
     *
     * @param target 目标对象
     * @param name   属性名
     * @return 属性值
     * @throws BeansException
     */
    public static Object getProperty(Object target, String name) throws BeansException {
        if (target == null) {
            return null;
        }
        BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(target);
        return beanWrapper.getPropertyValue(name);
    }

    /**
     * 设置对象属性, 支持多级属性名<code>a.b.c</code>
     *
     * @param target 目标对象
     * @param name   属性名
     * @param value  属性值
     * @throws BeansException
     */
    public static void setProperty(Object target, String name, Object value) throws BeansException {
        if (target != null) {
            BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(target);
            beanWrapper.setPropertyValue(name, value);
        }
    }

}
